package ldk.xposed.dexreplace

import android.content.Context
import android.os.FileUtils
import android.util.Log
import android.util.Xml
import ldk.xposed.dexreplace.utils.ShellUtil
import org.xmlpull.v1.XmlPullParser
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream

class Server(private val context: Context?) {
    companion object{
        private val TAG = "ldk.server"

        val STORAGE_FILE_DIR = "/data/app/ldk"
        val STORAGE_FILE_NAME = "dexreplace.xml"

        fun generateTmpFile(context: Context): File{
            val tmp = File(context.cacheDir, "dexreplace.xml")
            if (tmp.exists()){
                tmp.delete()
            }
            return tmp
        }

        fun generateTmpEmptyFile(context: Context): File{
            val tmp = generateTmpFile(context)
            FileUtils.stringToFile(tmp, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                    "<packages></packages>")
            return tmp
        }

        fun copyAndChown(source: String, destination: String){
            ShellUtil.runShell("cp $source $destination " +
                    "&& chmod 666 $destination " +
                    "&& chown system:system $destination")
        }
    }

    private val storageFile = File("$STORAGE_FILE_DIR/$STORAGE_FILE_NAME")

    init {
        val dir = File(STORAGE_FILE_NAME)
        if (!dir.exists() && context != null){
            ShellUtil.runShell("mkdir $STORAGE_FILE_DIR && chown system:system $STORAGE_FILE_DIR")
        }
        if (!storageFile.exists() && context != null){
            copyAndChown(generateTmpEmptyFile(context).absolutePath, storageFile.absolutePath)
        }
    }

    fun parseReplaceInfo(packageName: String): ReplaceDexInfo?{
        var result: ReplaceDexInfo? = null
        parser {
            if (it.packageName == packageName){
                result = it
                true
            }else{
                false
            }
        }
        return result
    }

    private fun parser(action: (ReplaceDexInfo)->Boolean){
        if (!storageFile.exists() || !storageFile.canRead()){
            Log.e(TAG, "storageFile 文件不存在或者不可读")
            return
        }
        val parser = Xml.newPullParser()
        var inputStream: FileInputStream? = null
        try{
            inputStream = FileInputStream(storageFile)
            parser.setInput(inputStream, "UTF-8")
            parser.next() // 解析过packages tag

            while (parser.next() == XmlPullParser.START_TAG && parser.name == "package"){
                val info = ReplaceDexInfo.parseReplaceDexInfo(parser)
                if (action.invoke(info)){
                    break
                }
            }
        }finally {
            inputStream?.close()
        }
    }

    fun parseReplaceInfos(): MutableMap<String, ReplaceDexInfo>{
        val resultMap = HashMap<String, ReplaceDexInfo>()
        parser {
            resultMap[it.packageName] = it
            false
        }
        return resultMap
    }

    fun updateReplaceInfo(infos: Map<String, ReplaceDexInfo>){
        val serializer = Xml.newSerializer()
        var outputStream: FileOutputStream ? = null
        val tmpFile = generateTmpFile(context!!)
        try{
            outputStream = FileOutputStream(tmpFile)
            serializer.setOutput(outputStream, "UTF-8")
            serializer.startDocument("UTF-8", true)
            serializer.startTag(null, "packages")
            infos.values.forEach {
                it.writeToXml(serializer)
            }
            serializer.endTag(null, "packages")
            serializer.endDocument()
            outputStream.flush()
        }finally {
            outputStream?.close()
        }

        copyAndChown(tmpFile.absolutePath, storageFile.absolutePath)
    }
}